package mysql.cs.handshake;

import common.MySQLPasswordEncrypter;
import common.MysqlCanalC;
import common.SecurityUtil;
import common.Util.ByteUtil;
import mysql.MysqlPacket;
import org.apache.commons.lang.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;

/**
 * 4              capability flags, CLIENT_PROTOCOL_41 always set
 * 4              max-packet size
 * 1              character set
 * string[23]     reserved (all [0])
 * string[NUL]    username
 *   if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA {
 * lenenc-int     length of auth-response
 * string[n]      auth-response
 *   } else if capabilities & CLIENT_SECURE_CONNECTION {
 * 1              length of auth-response
 * string[n]      auth-response
 *   } else {
 * string[NUL]    auth-response
 *   }
 *   if capabilities & CLIENT_CONNECT_WITH_DB {
 * string[NUL]    database
 *   }
 *   if capabilities & CLIENT_PLUGIN_AUTH {
 * string[NUL]    auth plugin name
 *   }
 *   if capabilities & CLIENT_CONNECT_ATTRS {
 * lenenc-int     length of all key-values
 * lenenc-str     key
 * lenenc-str     value
 *    if-more data in 'length of all key-values', more keys and value pairs
 *   }
 * @ClassName HandshakeResponse41
 * @Description TODO
 * @Author fengk
 * @Date 2018/8/27
 */

public class HandshakeResponse41 extends MysqlPacket {
    private String username;
    private String password;
    private byte   charsetNumber;
    private String databaseName;
    private int    serverCapabilities;
    private byte[] scrumbleBuff;

    public void encode(byte[] data) {
        // bypass since nowhere to use.
    }
    public byte[] decode() throws IOException {
        ByteArrayOutputStream out=new ByteArrayOutputStream();
        // 1. write client_flags
        // 1|4|512=0x200|1024=0x400=|8192=0x2000|32768=0x8000
        /**
         * CLIENT_LONG_PASSWORD 0x00000001
         * CLIENT_LONG_FLAG  0x00000004
         * CLIENT_PROTOCOL_41  0x00000200
         * CLIENT_INTERACTIVE 0x00000400
         * CLIENT_TRANSACTIONS 0x00002000
         * CLIENT_SECURE_CONNECTION 0x00008000
         * CLIENT_MULTI_STATEMENTS  0x00010000
         */
        //capability
        ByteUtil.writeUnsignedIntLittleEndian(1 | 4 | 0x200 | 0x400 |0x2000| 0x8000 | 0x00010000, out);
        //max packet size
        ByteUtil.writeUnsignedIntLittleEndian(MysqlCanalC.MAX_PACKET_LENGTH,out);

        out.write(this.charsetNumber);
        //保留23个字节
        out.write(new byte[23]);
        ByteUtil.writeNullTerminatedString(getUsername(),out);
        if(StringUtils.isEmpty(getPassword())){
            out.write(0x00);
        }else{
            try {
                byte[] encryptedPassword;
                /*411 只有随机数part1 长度只有8*/
                if(scrumbleBuff.length<=8){
                    encryptedPassword=MySQLPasswordEncrypter.scramble411(getPassword().getBytes(),scrumbleBuff);
                }else {
                    encryptedPassword= SecurityUtil.scramble411_2(getPassword().getBytes(),scrumbleBuff);
                }

                ByteUtil.writeBinaryCodedLengthBytes(encryptedPassword,out);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        }
        //todo ???
        if(getDatabaseName()!=null){
            ByteUtil.writeNullTerminatedString(getDatabaseName(),out);
        }
        return out.toByteArray();
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public byte getCharsetNumber() {
        return charsetNumber;
    }

    public void setCharsetNumber(byte charsetNumber) {
        this.charsetNumber = charsetNumber;
    }

    public String getDatabaseName() {
        return databaseName;
    }

    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }

    public int getServerCapabilities() {
        return serverCapabilities;
    }

    public void setServerCapabilities(int serverCapabilities) {
        this.serverCapabilities = serverCapabilities;
    }

    public byte[] getScrumbleBuff() {
        return scrumbleBuff;
    }

    public void setScrumbleBuff(byte[] scrumbleBuff) {
        this.scrumbleBuff = scrumbleBuff;
    }
}
